﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(CusSkinMeshRdr))]
[CanEditMultipleObjects]
public class CusSkinMeshRdrEditor : E_ShowButtons<CusSkinMeshRdr> { }
#endif
/// Custom Skinned Mesh Renderer
public class CusSkinMeshRdr : MonoBehaviour
{
    public Mesh mesh;
    public Transform[] bones;
    public List<BoneWeights> boneWgts;
    public Material material;
    public Vector3[] normalsOrigin;
    public bool recalculateNormalsInUpdate = true;
    public bool recalculateTangentsInUpdate = false;
    public CommandBuffer buf;
    public Transform rig
    {
        get
        {
            return bones == null || bones.Length == 0
                || bones[0] == null ? null : bones[0].parent;
        }
    }
    public void CB_DrawMesh()
    {
        buf = new CommandBuffer();
        var m = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, Vector3.one);
        buf.DrawMesh(mesh, m, material, 0);
        Camera.main.AddCommandBuffer(CameraEvent.AfterForwardOpaque, buf);
#if UNITY_EDITOR
        var sv = SceneView.lastActiveSceneView;
        if (sv == null) return;
        sv.camera.AddCommandBuffer(CameraEvent.AfterForwardOpaque, buf);
#endif
    }
    private void OnEnable()
    {
        try
        {
            if (buf == null) return;
            Camera.main.AddCommandBuffer(CameraEvent.AfterForwardOpaque, buf);
#if UNITY_EDITOR
            var sv = SceneView.lastActiveSceneView;
            if (sv == null) return;
            sv.camera.AddCommandBuffer(CameraEvent.AfterForwardOpaque, buf);
#endif
        }
        catch { }
    }
    private void OnDisable()
    {
        try
        {
            if (buf == null) return;
            Camera.main.RemoveCommandBuffer(CameraEvent.AfterForwardOpaque, buf);
#if UNITY_EDITOR
            var sv = SceneView.lastActiveSceneView;
            if (sv == null) return;
            sv.camera.RemoveCommandBuffer(CameraEvent.AfterForwardOpaque, buf);
#endif
        }
        catch { }
    }
    public void Update()
    {
        if (mesh != null)
        {
            Vector3[] vs;
            UpdateData(out vs);
            mesh.vertices = vs;
            if (recalculateNormalsInUpdate) mesh.RecalculateNormals();
            if (recalculateTangentsInUpdate) mesh.RecalculateTangents();

            //Vector3[] vs,ns;
            //UpdateData(out vs,out ns);
            //UpdateData(out vs);
            //mesh.vertices = vs;
            //mesh.normals = ns;

#if UNITY_EDITOR
            //不知道为什么在场景Camera里不显示
            //Graphics.DrawMesh(mesh, Vector3.zero, Quaternion.identity, material, 0);
#endif
        }
    }
    void UpdateData(out Vector3[] vs)
    {
        vs = new Vector3[mesh.vertexCount];
        Vector3 v;
        for (int i = 0; i < mesh.vertexCount; i++)
        {
            var bi = boneWgts[i];
            v = Vector3.zero;
            foreach (Weight bone in bi.wgts)
            {
                v += bone.t.TransformPoint(bone.delta) * bone.weight;
            }
            vs[i] = v;
        }
    }
    public void UpdateData(out Vector3[] vs, out Vector3[] ns)
    {
        vs = new Vector3[mesh.vertexCount];
        ns = new Vector3[mesh.vertexCount];
        Vector3 v, n;
        for (int i = 0; i < mesh.vertexCount; i++)
        {
            var bi = boneWgts[i];
            v = Vector3.zero;
            n = Vector3.zero;
            foreach (Weight bone in bi.wgts)
            {
                v += bone.t.TransformPoint(bone.delta) * bone.weight;
                n += bone.t.TransformDirection(normalsOrigin[i]) * bone.weight;
            }
            vs[i] = v;
            ns[i] = n;
        }
    }
    int GetBoneIdx(Transform t)
    {
        //if (t == null) return 0; // 目前的机制通常不会出现受影响骨骼数少于4的情况。
        // 总骨骼数少于4应该会出现，留个TODO
        for (int i = 0; i < bones.Length; i++)
        {
            if (bones[i] == t) return i;
        }
        throw new System.Exception("UnMatch Bone");
    }
    [ShowButton]
    public void SwitchToSMR()
    {
        var vCount = boneWgts.Count;
        var weights = new BoneWeight[vCount];
        for (int v = 0; v < vCount; v++)
        {
            var bw = boneWgts[v];
            weights[v].boneIndex0 = GetBoneIdx(bw.wgts[0].t);
            weights[v].weight0 = bw.wgts[0].weight;

            weights[v].boneIndex1 = GetBoneIdx(bw.wgts[1].t);
            weights[v].weight1 = bw.wgts[1].weight;

            // 机制的最低骨骼数是2根

            if (bw.wgts.Count > 2 && bw.wgts[2] != null)
            {
                weights[v].boneIndex2 = GetBoneIdx(bw.wgts[2].t);
                weights[v].weight2 = bw.wgts[2].weight;
            }
            if (bw.wgts.Count > 3 && bw.wgts[3] != null)
            {
                weights[v].boneIndex3 = GetBoneIdx(bw.wgts[3].t);
                weights[v].weight3 = bw.wgts[3].weight;
            }
        }
        var bindPoses = new Matrix4x4[bones.Length];
        for (int i = 0; i < bones.Length; i++)
        {
            bindPoses[i] = bones[i].worldToLocalMatrix * transform.localToWorldMatrix;
        }
        mesh.bindposes = bindPoses;
        mesh.boneWeights = weights;
        var smr = gameObject.AddComponent<SkinnedMeshRenderer>();
        smr.sharedMaterial = material;
        smr.bones = bones;
        smr.rootBone = rig;
        smr.sharedMesh = mesh;

        var mc = gameObject.AddComponent<MeshCollider>();
        mc.sharedMesh = mesh;

        this.enabled = false;
        Destroy(this);
    }
}
